# sklearn是学习人工智能技术技术必须掌握的库，其中有一份糖尿病患者的数据sklearn.datasets中的load_diabetes，请按照以下要求，正确实现线性回归：
# 1)	导入相关的库，包括但不限于sklearn，numpy等等
import warnings
warnings.filterwarnings('ignore')
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf
tf.compat.v1.logging.set_verbosity(40)
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

np.random.seed(123)

# 加载数据--以元组格式加载
(x_train,y_train),(x_test,y_test) = mnist.load_data()
# 将特征数据转换为二维数据
x_test = x_test.reshape(-1,28*28)
# 将数据类型转换为浮点型并且进行归一化
x_test = x_test.astype('float')/255

for i,y in enumerate(y_test):
    if y==5:
        y_test[i] = 1
    else:
        y_test[i] = 0

y_test = np.reshape(y_test,(-1,1))
x_train,x_test,y_train,y_test = train_test_split(x_test,y_test,train_size=0.7)

# 8)	编写一个类，需要包含：代价函数，训练函数，测试函数
class MyModel():
    def __init__(self,x_train,y_train,iter=5000,alpha=0.1):
        self.x_train = x_train
        self.y_train = y_train
        self.iter = iter
        self.alpha = alpha

        self.m,self.n = x_train.shape
        self.w = np.random.randn(self.n,1)
        self.b = np.random.randn(1)
        self.J = np.zeros(iter)

    # 9)    代价函数使用MSE均方误差
    def my_cost(self,h, y):
        return  -np.mean(y*np.log(h)+(1-y)*np.log(1-h))

    # 10)    正确在类中编写训练的函数，设置迭代次数为20000，学习率为0.1
    def my_train(self):
        for i in range(self.iter):
            z_train = self.my_predict(self.x_train, self.w,self.b)
            h_train = self.my_sigmoid(z_train)
            self.J[i] = self.my_cost(h_train, self.y_train)

            dw = 1 / self.m * self.x_train.T.dot(h_train - y_train)
            self.w -= self.alpha * dw

            db = np.mean(h_train - self.y_train)
            self.b -= self.alpha * db

            # 13)    每500次打印，并输出代价
            if i%500==0:
                print(f'迭代次数:{i},代价值:{self.J[i]:.5f}')

        return self.J, self.w,self.b

    # 11)    在类中编写出预测的方法
    def my_predict(self,x,w,b):
        return x.dot(w)+b

    def my_sigmoid(self,z):
        return 1 / (1 + np.exp(-z))

    def my_score(self,h, y):
        s = 0
        for i in range(len(h)):
            if h[i] > 0.5 and y[i] == 1:
                s += 1
            elif h[i] <= 0.5 and y[i] == 0:
                s += 1
        return s / len(h)

if __name__ == '__main__':
    # 12)    实例化该类，并进行训练
    model = MyModel(x_train,y_train)
    J,w,b = model.my_train()
    print(f'权重:{w},偏置:{b}')

    # 14)    绘出训练的学习曲线
    plt.plot(J)
    plt.show()

    h_test = model.my_predict(x_test,w,b)
    test_score = model.my_score(h_test,y_test)
    print(test_score)

    print(np.mean(y_test==(h_test>0.5)))